import { formatSuccess, failed } from '@gnlab/utils'
import type { RequiredFields } from '@gnlab/utils'
import pick from 'lodash/pick'
import type { LoaderData } from '@/lib/infinite-loader'
import { InfiniteLoader, LoadStatus } from '@/lib/infinite-loader'
import { API } from '@/services/common/api'
import { splitContactId } from '@/services/contacts'
import type {
  OnlineChatMessage,
  ChatRecord,
  TextRecord,
  LinkCardContent,
  MiniProgramContent,
  VideonumContent,
  PersonCardContent,
} from './definition'
import { parseOnlineChatRecord, ChatRecordType, chatRecordToText } from './definition'

/**
 * =========================
 * 拉取记录
 * =========================
 */

/**
 * 拉取在线聊天记录
 */
export async function fetchOnlineRecords(params: {
  wxid: string
  conversationId: string
  lastTimestamp?: number
  previous?: boolean // 仅在传入 lastTimestamp 时生效，加载时间戳之前的还是之后的聊天记录
  pageSize?: number
}) {
  const { wxid, conversationId, lastTimestamp, previous, pageSize = 10 } = params
  return API('/ChatHistory/ChatHistorys', {
    data: {
      wxid,
      conversationIds: [conversationId],
      lastTimestamp,
      type: lastTimestamp !== undefined && previous !== undefined ? (previous ? 0 : 1) : undefined,
      pageSize,
    },
    formatter(messages: OnlineChatMessage[]) {
      const records = messages.map(parseOnlineChatRecord)
      if (previous !== false) records.reverse() // previous=false 时，后端正序返回聊天记录；其他情况后端倒序返回聊天记录，需手动翻转
      return {
        records,
        full: !records.length,
      }
    },
  })
}

/**
 * 实现聊天记录的翻页加载
 */
export class HistoryLoader extends InfiniteLoader<HistoryConditions, ChatRecord[]> {
  constructor(
    readonly wxid: string,
    readonly conversationId: string,
  ) {
    super()
  }

  getInitialData() {
    return getHistoryLoaderInitialData()
  }

  mergeItems(prev: ChatRecord[], current: ChatRecord[]) {
    // 历史记录是一次次加载“更早的记录”，因此每次加载到的都放在更前面
    return [...current, ...prev]
  }

  request() {
    const items = this._data.value.items
    const lastTimestamp = items[0]?.timestamp
    const pageSize = 40 // 不能太小，要保证能填满一页界面。20条基本可填满一页，这里翻倍是为了避免频繁重新加载
    const promise = formatSuccess(
      fetchOnlineRecords({
        wxid: this.wxid,
        conversationId: this.conversationId,
        lastTimestamp,
        pageSize,
      }),
      data => ({
        items: data.records,
        full: data.records.length < pageSize,
      }),
    )
    return {
      conditions: { lastTimestamp: lastTimestamp ?? null },
      promise,
    }
  }
}

export function getHistoryLoaderInitialData(): HistoryLoaderData {
  return {
    status: LoadStatus.Idle,
    conditions: { lastTimestamp: null },
    items: [],
  }
}

export interface HistoryConditions {
  lastTimestamp: number | null
}
export type HistoryLoaderData = LoaderData<HistoryConditions, ChatRecord[]>

/**
 * 搜索聊天记录
 */
export interface MatchedHistory {
  wxid: string
  conversationId: string
  records: TextRecord[]
}

interface RawMatchedHistory {
  wxid: string
  conversation_id: string
  chatHistorys: OnlineChatMessage[]
}

export async function searchHistory(params: {
  keyword: string
  wxids: string[]
  conversationIds: string[]
  page?: number
  pageSize?: number
}) {
  const { keyword, wxids, conversationIds, page = 1, pageSize = 100 } = params
  return API('/ChatHistory/SearchChatHistorys', {
    data: {
      searchKey: keyword,
      wxIds: wxids,
      conversationIds,
      pageIndex: page,
      pageSize,
    },
    formatter(raw: RawMatchedHistory[]): MatchedHistory[] {
      return raw
        .map(item => {
          // 后端可能意外返回非文本记录（例如 ContactNotify 记录），要过滤一下
          const records = item.chatHistorys
            .map(parseOnlineChatRecord)
            .filter((record): record is TextRecord => record.type === ChatRecordType.Text)
          return {
            wxid: item.wxid,
            conversationId: item.conversation_id,
            records,
          }
        })
        .filter(item => item.records.length)
    },
  })
}

/**
 * =========================
 * 发送消息
 * =========================
 */
async function sendText(wxid: string, conversationId: string, content: string) {
  return API<{ msgId: string }>('/msg/text', {
    data: { wxid, conversationId, content },
  })
}
async function sendRoomAt(
  wxid: string,
  conversationId: string,
  content: string,
  atList: { userId: string; nickname: string }[],
) {
  for (const item of atList) {
    const tagText = `@${item.nickname} `
    content = content.replace(tagText, '{$@} ')
  }
  return API<{ msgId: string }>('/msg/roomAt', {
    data: { wxid, conversationId, content, atList: atList.map(v => v.userId) },
  })
}
async function sendQuote(
  wxid: string,
  conversationId: string,
  content: string,
  quoted: { record: ChatRecord; nickname: string },
) {
  return API<{ msgId: string }>('/msg/quote', {
    data: {
      wxid,
      conversationId,
      content,
      serverId: quoted.record.serverId,
      quote: chatRecordToText(quoted.record),
      userName: quoted.nickname,
    },
  })
}
async function sendImage(wxid: string, conversationId: string, fileUrl: string) {
  return API<{ msgId: string }>('/msg/image', {
    data: { wxid, conversationId, fileUrl },
  })
}
async function sendVideo(wxid: string, conversationId: string, fileUrl: string) {
  return API<{ msgId: string }>('/msg/video', {
    data: { wxid, conversationId, fileUrl },
  })
}
async function sendFile(wxid: string, conversationId: string, fileUrl: string) {
  return API<{ msgId: string }>('/msg/file', {
    data: { wxid, conversationId, fileUrl },
  })
}
async function sendEmotion(wxid: string, conversationId: string, fileUrl: string) {
  return API<{ msgId: string }>('/msg/gif', { data: { wxid, conversationId, fileUrl } })
}
async function sendLinkCard(
  wxid: string,
  conversationId: string,
  content: RequiredFields<Partial<LinkCardContent>, 'title'>,
) {
  const { title, desc = '', image: imageUrl = '', url = '' } = content
  return API<{ msgId: string }>('/msg/link', {
    data: {
      wxid,
      conversationId,
      title,
      desc,
      imageUrl,
      url,
    },
  })
}
async function sendPersonCard(wxid: string, conversationId: string, record: PersonCardContent) {
  return API<{ msgId: string }>('/msg/personCard', {
    data: {
      wxid,
      conversationId,
      personCardUserId: record.userId,
      avatar: record.avatar,
      nickname: record.nickname,
    },
  })
}
async function sendMiniProgram(wxid: string, conversationId: string, mp: MiniProgramContent) {
  const data = {
    wxid,
    conversationId,
    ...pick(mp, 'username', 'appid', 'appname', 'appicon', 'title', 'page_path', 'cover_path'),
  }
  // 经多个客户实测，不带 @app 后缀发送小程序会打不开
  if (!data.username.endsWith('@app')) data.username = data.username + '@app'
  return API<{ msgId: string }>('/msg/miniProgram', { data })
}
async function sendVideonum(wxid: string, conversationId: string, videonum: VideonumContent) {
  const data = {
    wxid,
    conversationId,
    ...pick(
      videonum,
      'avatar',
      'nickname',
      'desc',
      'cover_url',
      'thumb_url',
      'extras',
      'feed_type',
      'url',
    ),
  }
  return API<{ msgId: string }>('/msg/videoNum', { data })
}
async function sendAudio(wxid: string, conversationId: string, url: string) {
  const data = {
    wxid,
    conversationId,
    fileUrl: url,
  }
  return API<{ msgId: string }>('/msg/voice', { data })
}

/**
 * 通用的消息发送方法
 * - 执行了发送则返回发送请求；
 * - 否则返回提示信息
 *
 * 若消息发送用到了 quoted，会触发 onQuoteUsed 回调
 */
export async function send(
  ownerWxid: string,
  conversationId: string,
  record: ChatRecord,
  quoted?: { record: ChatRecord; nickname: string },
  onQuoteUsed?: () => void,
) {
  if (!record.isSend) return failed('无法发送“接收”类型的消息')

  switch (record.type) {
    case ChatRecordType.Text: {
      if (record.content.atList.length)
        return sendRoomAt(ownerWxid, conversationId, record.content.text, record.content.atList)
      if (quoted) {
        onQuoteUsed?.()
        return sendQuote(ownerWxid, conversationId, record.content.text, quoted)
      }
      return sendText(ownerWxid, conversationId, record.content.text)
    }
    case ChatRecordType.Image:
      return sendImage(ownerWxid, conversationId, record.content.url)
    case ChatRecordType.Video:
      return sendVideo(ownerWxid, conversationId, record.content.url)
    case ChatRecordType.File:
      return sendFile(ownerWxid, conversationId, record.content.url)
    case ChatRecordType.Emotion:
      return sendEmotion(ownerWxid, conversationId, record.content.url)
    case ChatRecordType.LinkCard:
      return sendLinkCard(ownerWxid, conversationId, record.content)
    case ChatRecordType.PersonCard:
      return sendPersonCard(ownerWxid, conversationId, record.content)
    case ChatRecordType.MiniProgram:
      return sendMiniProgram(ownerWxid, conversationId, record.content)
    case ChatRecordType.Videonum:
      return sendVideonum(ownerWxid, conversationId, record.content)
    case ChatRecordType.Audio:
      return sendAudio(ownerWxid, conversationId, record.content.url)
    default:
      return failed(`不支持的消息类型：${record.type}，无法发送`)
  }
}

/**
 * =========================
 * 更新消息
 * =========================
 */

/**
 * 撤回消息
 */
export async function recall(msgId: string) {
  return API('/msg/recall', { data: { msgId } })
}

/**
 * 重新获取图片消息的图片 URL
 */
export async function reloadImage(msgId: string) {
    return API('/msg/FindFilePath', {
        data: { msgId },
        formatter(raw: { FilePath: string }) { return raw.FilePath }
    })
}

/**
 * =========================
 * 其他
 * =========================
 */

/**
 * 基于名片消息添加好友
 */
export async function applyByPersonCard(data: {
  wxid: string // 接收到此名片消息（同时也是要加好友）的企业微信的 wxid
  conversationId: string // 此名片来自哪个会话
  senderWxid: string // 消息发送者的 wxid（群里发送的名片消息无法通过 conversationId 确定发送者 wxid，因此需要传递此参数）
  personCardUserId: string // 名片中的 userId
  message: string // 加好友验证消息
}) {
  const { wxid, conversationId, senderWxid, personCardUserId, message } = data
  return API('/Contact/PersonCardApplyContact', {
    data: {
      wxid,
      conversationId,
      sender: senderWxid,
      contactUserId: personCardUserId,
      verify: message,
    },
    formatter() {},
  })
}

/**
 * 重置未读消息数
 */
export async function resetUnread(contacts: { id: string; conversationId: string }[]) {
  return API('/Contact/ClearNewMsgCount', {
    data: {
      conversationInfos: contacts.map(({ id, conversationId }) => {
        const [ownerWxid] = splitContactId(id)
        return { WxId: ownerWxid, ConversationId: conversationId }
      }),
    },
  })
}

/**
 * 让已读的联系人回到“未读”状态
 */
export async function markUnread(contacts: { id: string; conversationId: string }[]) {
  return API('/Contact/AddNewMsgCount', {
    data: {
      conversationInfos: contacts.map(({ id, conversationId }) => {
        const [ownerWxid] = splitContactId(id)
        return { WxId: ownerWxid, ConversationId: conversationId }
      }),
    },
  })
}

/**
 * 清除预警消息
 */
export async function clearWarn(contactId: string, conversationId: string) {
  const [ownerWxid] = splitContactId(contactId)
  return API('/Contact/ClearWarnMsg', {
    data: { WxId: ownerWxid, ConversationId: conversationId },
  })
}
